#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "bsp_lcd.h"
#include "esp_log.h"
#include "esp_heap_caps.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "driver/spi_master.h"

static const char *TAG = "bsp_lcd";

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// Please update the following configuration according to your LCD spec //////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define EXAMPLE_LCD_PIXEL_CLOCK_HZ     (2*1000*1000)

#define EXAMPLE_LCD_BK_LIGHT_ON_LEVEL  1
#define EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL !EXAMPLE_LCD_BK_LIGHT_ON_LEVEL
#define EXAMPLE_PIN_NUM_DATA0          6
#define EXAMPLE_PIN_NUM_DATA1          7
#define EXAMPLE_PIN_NUM_DATA2          8
#define EXAMPLE_PIN_NUM_DATA3          9
#define EXAMPLE_PIN_NUM_DATA4          10
#define EXAMPLE_PIN_NUM_DATA5          11
#define EXAMPLE_PIN_NUM_DATA6          12
#define EXAMPLE_PIN_NUM_DATA7          13
#define EXAMPLE_PIN_NUM_DATA8          14
#define EXAMPLE_PIN_NUM_DATA9          15
#define EXAMPLE_PIN_NUM_DATA10         16
#define EXAMPLE_PIN_NUM_DATA11         17
#define EXAMPLE_PIN_NUM_DATA12         18
#define EXAMPLE_PIN_NUM_DATA13         19
#define EXAMPLE_PIN_NUM_DATA14         20
#define EXAMPLE_PIN_NUM_DATA15         21

#define EXAMPLE_PIN_NUM_PCLK           5
#define EXAMPLE_PIN_NUM_CS             3
#define EXAMPLE_PIN_NUM_DC             4
#define EXAMPLE_PIN_NUM_RST            -1
#define EXAMPLE_PIN_NUM_BK_LIGHT       1

// The pixel number in horizontal and vertical
#define EXAMPLE_LCD_H_RES              240
#define EXAMPLE_LCD_V_RES              320

#define EXAMPLE_LCD_CMD_BITS           8
#define EXAMPLE_LCD_PARAM_BITS         8

/**
 * @brief 初始化LCD背光
 * 
 * 配置并初始化LCD背光控制引脚，设置背光亮度
 * 
 * @param brightness_percent 背光亮度百分比 (0-100)
 */
void bsp_display_backlight_set(int brightness_percent)
{
    if (brightness_percent > 100) {
        brightness_percent = 100;
    }
    if (brightness_percent < 0) {
        brightness_percent = 0;
    }

    ESP_LOGI(TAG, "Setting LCD backlight: %d%%", brightness_percent);
    gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, brightness_percent > 0 ? EXAMPLE_LCD_BK_LIGHT_ON_LEVEL : EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL);
}

/**
 * @brief 初始化LCD面板IO
 * 
 * 配置并初始化LCD的8080并行接口，设置数据引脚和控制引脚
 * 
 * @param io_handle IO句柄指针，用于返回创建的IO句柄
 * @return esp_err_t ESP操作结果
 */
static esp_err_t init_i80_bus(esp_lcd_panel_io_handle_t *io_handle)
{
    ESP_LOGI(TAG, "Initialize Intel 8080 bus");
    esp_lcd_i80_bus_handle_t i80_bus = NULL;
    esp_lcd_i80_bus_config_t bus_config = {
        .clk_src = LCD_CLK_SRC_DEFAULT,
        .dc_gpio_num = EXAMPLE_PIN_NUM_DC,
        .wr_gpio_num = EXAMPLE_PIN_NUM_PCLK,
        .data_gpio_nums = {
            EXAMPLE_PIN_NUM_DATA0,
            EXAMPLE_PIN_NUM_DATA1,
            EXAMPLE_PIN_NUM_DATA2,
            EXAMPLE_PIN_NUM_DATA3,
            EXAMPLE_PIN_NUM_DATA4,
            EXAMPLE_PIN_NUM_DATA5,
            EXAMPLE_PIN_NUM_DATA6,
            EXAMPLE_PIN_NUM_DATA7,
            EXAMPLE_PIN_NUM_DATA8,
            EXAMPLE_PIN_NUM_DATA9,
            EXAMPLE_PIN_NUM_DATA10,
            EXAMPLE_PIN_NUM_DATA11,
            EXAMPLE_PIN_NUM_DATA12,
            EXAMPLE_PIN_NUM_DATA13,
            EXAMPLE_PIN_NUM_DATA14,
            EXAMPLE_PIN_NUM_DATA15,
        },
        .bus_width = 16,
        .max_transfer_bytes = EXAMPLE_LCD_H_RES * EXAMPLE_LCD_V_RES * sizeof(uint16_t), // 传输20行数据
        .psram_trans_align = 64,
        .sram_trans_align = 4,
    };
    
    esp_err_t ret = esp_lcd_new_i80_bus(&bus_config, &i80_bus);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to initialize I80 bus: %s", esp_err_to_name(ret));
        return ret;
    }
    
    esp_lcd_panel_io_i80_config_t io_config = {
        .cs_gpio_num = EXAMPLE_PIN_NUM_CS,
        .pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
        .trans_queue_depth = 10,
        .dc_levels = {
            .dc_idle_level = 0,
            .dc_cmd_level = 0,
            .dc_data_level = 1,
        },
        .flags = {
            .swap_color_bytes = 0, // 不交换颜色字节
        },
        .lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS,
        .lcd_param_bits = EXAMPLE_LCD_PARAM_BITS,
    };
    
    ret = esp_lcd_new_panel_io_i80(i80_bus, &io_config, io_handle);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to create panel IO: %s", esp_err_to_name(ret));
        return ret;
    }
    
    return ESP_OK;
}

/**
 * @brief 初始化LCD面板
 * 
 * 配置并初始化ST7789 LCD面板控制器
 * 
 * @param io_handle IO句柄
 * @param panel_handle 面板句柄指针，用于返回创建的面板句柄
 * @return esp_err_t ESP操作结果
 */
static esp_err_t init_lcd_panel(esp_lcd_panel_io_handle_t io_handle, esp_lcd_panel_handle_t *panel_handle)
{
    ESP_LOGI(TAG, "Install LCD driver of ST7789");
    esp_lcd_panel_dev_config_t panel_config = {
        .reset_gpio_num = EXAMPLE_PIN_NUM_RST,
        .color_space = ESP_LCD_COLOR_SPACE_BGR,  // 使用BGR颜色空间解决RGB顺序问题
        .bits_per_pixel = 16,
    };
    
    esp_err_t ret = esp_lcd_new_panel_st7789(io_handle, &panel_config, panel_handle);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to create ST7789 panel: %s", esp_err_to_name(ret));
        return ret;
    }

    ret = esp_lcd_panel_reset(*panel_handle);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to reset panel: %s", esp_err_to_name(ret));
        return ret;
    }
    
    ret = esp_lcd_panel_init(*panel_handle);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to initialize panel: %s", esp_err_to_name(ret));
        return ret;
    }
    
    ESP_ERROR_CHECK(esp_lcd_panel_invert_color(*panel_handle, false));      //颜色反转
    ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(*panel_handle, false));           //屏幕旋转  横屏/竖屏
    ESP_ERROR_CHECK(esp_lcd_panel_set_gap(*panel_handle, 0, 0));            //设置偏移
    ESP_ERROR_CHECK(esp_lcd_panel_mirror(*panel_handle, true, false));      //坐标镜像
    ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(*panel_handle, true));        //屏幕打开
    return ESP_OK;
}

/**
 * @brief 填充屏幕颜色
 * 
 * 使用指定颜色填充整个屏幕
 * 
 * @param panel_handle LCD面板句柄
 * @param color 填充颜色 (RGB565格式)
 */
void bsp_lcd_fill_screen(esp_lcd_panel_handle_t panel_handle, uint16_t color)
{
    // 使用较小的缓冲区以减少内存使用
    uint16_t *buffer = heap_caps_malloc(100 * sizeof(uint16_t), MALLOC_CAP_DMA);
    if (buffer == NULL) {
        ESP_LOGE(TAG, "Not enough memory for buffer");
        return;
    }
    
    // 填充缓冲区
    for (int i = 0; i < 100; i++) {
        buffer[i] = color;
    }
    
    // 绘制每一行（使用较小的块）
    for (int y = 0; y < EXAMPLE_LCD_V_RES; y++) {
        for (int x = 0; x < EXAMPLE_LCD_H_RES; x += 100) {
            int chunk_width = (x + 100 > EXAMPLE_LCD_H_RES) ? (EXAMPLE_LCD_H_RES - x) : 100;
            esp_lcd_panel_draw_bitmap(panel_handle, x, y, x + chunk_width, y + 1, buffer);
        }
    }
    
    free(buffer);
}

/**
 * @brief 初始化LCD显示
 * 
 * 初始化整个LCD显示系统，包括IO总线和面板
 * 
 * @return esp_lcd_panel_handle_t LCD面板句柄
 */
esp_lcd_panel_handle_t bsp_lcd_initialize(void)
{
#if EXAMPLE_PIN_NUM_BK_LIGHT >= 0
    ESP_LOGI(TAG, "Turn off LCD backlight");
    gpio_config_t bk_gpio_config = {
        .mode = GPIO_MODE_OUTPUT,
        .pin_bit_mask = 1ULL << EXAMPLE_PIN_NUM_BK_LIGHT
    };
    ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));
    gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL);
#endif // EXAMPLE_PIN_NUM_BK_LIGHT >= 0

    esp_lcd_panel_io_handle_t io_handle = NULL;
    ESP_ERROR_CHECK(init_i80_bus(&io_handle));

    esp_lcd_panel_handle_t panel_handle = NULL;
    ESP_ERROR_CHECK(init_lcd_panel(io_handle, &panel_handle));

    // Stub: user can flush pre-defined pattern to the screen before we turn on the screen or backlight

    ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));

#if EXAMPLE_PIN_NUM_BK_LIGHT >= 0
    ESP_LOGI(TAG, "Turn on LCD backlight");
    gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_ON_LEVEL);
#endif // EXAMPLE_PIN_NUM_BK_LIGHT >= 0

    return panel_handle;
}